﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Interactivity;
using System.ComponentModel.Composition;
using System.Windows;
using System.ComponentModel.Composition.Hosting;

namespace ZOIL.Library.Behaviors
{
    public interface IRuntimeBehavior
    {
        DependencyProperty IsEnabledDependencyProperty { get; }
    }

    public class RuntimeBehavior<T, TB> : Behavior<T>, IRuntimeBehavior
        where TB : PluginBehavior<T>
        where T : DependencyObject
    {
        private readonly CompositionContainer _container;
        private readonly AggregateCatalog _catalog;
        private readonly List<Tuple<RoutedEvent, Delegate>> _eventsToBeRegisteredOnAttaching;

        #region dependency properties

        #region IsEnabled

        /// <summary>
        /// IsEnabled Dependency Property
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof(bool), typeof(RuntimeBehavior<T, TB>), new FrameworkPropertyMetadata(false, OnIsEnabledChanged));

        /// <summary>
        /// Gets or sets the IsEnabled property. This dependency property 
        /// indicates ....
        /// </summary>
        public bool IsEnabled
        {
            get { return (bool)GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }

        /// <summary>
        /// Handles changes to the IsEnabled property.
        /// </summary>
        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var target = (RuntimeBehavior<T, TB>)d;
            var oldIsEnabled = (bool)e.OldValue;
            var newIsEnabled = target.IsEnabled;
            target.OnIsEnabledChanged(oldIsEnabled, newIsEnabled);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the IsEnabled property.
        /// </summary>
        protected virtual void OnIsEnabledChanged(bool oldIsEnabled, bool newIsEnabled)
        {

        }

        #endregion

        #endregion

        [ImportMany]
        protected IEnumerable<TB> PluginBehaviors
        {
            get;
            set;
        }

        public RuntimeBehavior()
        {
            // Initialize plugin behaviors
            _catalog = new AggregateCatalog();
            _catalog.Catalogs.Add(new AssemblyCatalog(typeof(RuntimeBehavior<T, TB>).Assembly));
            _container = new CompositionContainer(_catalog);

            var currentAssembly = GetType().Assembly;

            if (_catalog.Catalogs.OfType<AssemblyCatalog>().All(catalog => catalog.Assembly != currentAssembly))
                _catalog.Catalogs.Add(new AssemblyCatalog(GetType().Assembly));

            //Fill the imports of this object
            try
            {
                _container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());
            }

            foreach (var behavior in PluginBehaviors)
            {
                behavior.ParentBehavior = this;
            }

            _eventsToBeRegisteredOnAttaching = new List<Tuple<RoutedEvent, Delegate>>();

        }

        #region overrides

        protected override void OnAttached()
        {
            base.OnAttached();

            foreach (var tuple in _eventsToBeRegisteredOnAttaching)
            {
                RoutedEventHelper.AddHandler(AssociatedObject, tuple.Item1, tuple.Item2);
            }

            foreach (TB behavior in PluginBehaviors)
            {
                behavior.Attach(AssociatedObject);
            }
        }

        protected override void OnDetaching()
        {
            foreach (var tuple in _eventsToBeRegisteredOnAttaching)
            {
                RoutedEventHelper.RemoveHandler(AssociatedObject, tuple.Item1, tuple.Item2);
            }

            foreach (var behavior in PluginBehaviors)
            {
                behavior.Detach();
            }

            base.OnDetaching();
        }

        #endregion

        protected void AddRoutedEventHandler(RoutedEvent evt, Delegate handler)
        {
            _eventsToBeRegisteredOnAttaching.Add(new Tuple<RoutedEvent, Delegate>(evt, handler));
        }

        protected void RemoveRoutedEventHandler(RoutedEvent evt, Delegate handler)
        {
            RoutedEventHelper.RemoveHandler(AssociatedObject, evt, handler);
        }

        #region IRuntimeBehavior

        public DependencyProperty IsEnabledDependencyProperty
        {
            get { return IsEnabledProperty; }
        }

        #endregion
    }
}
